/**
 * @Description: 拖动box基础类
 * @author Beon
 * @date 2021/6/28
*/
import Plant from './plant';

/**
 * 生命周期: @important 每一个生命函数若返回值为boolean类型的false,则不执行当此生命周期(mouseup会执行取消move)
 * beforeDrop(mouseDown)
 * dropMove(mouseMove) !! 不支持异步
 * dropOver(mouseUp)
 */
export interface EventFunction {
	(this: DropBox, e: MouseEvent): any
}
export enum DealListDealType {
	'add',
	'del'
}
export default abstract class DropBox{
	// 可拖动的id
	id: string
	// 绑定dom对象
	$el: HTMLElement
	// 生命周期执行数组
	beforeDropList: Array<EventFunction> = []
	dropMoveList: Array<EventFunction> = []
	dropOverList: Array<EventFunction> = []
	// 修改执行方法bind
	beforeDropBind: (this: HTMLElement, ev: MouseEvent) => void
	dropMoveBind: (this: HTMLElement, ev: MouseEvent) => void
	dropOverBind: (this: HTMLElement, ev: MouseEvent) => void
	// 全部拖动组件
	private static dropBoxList: Record<string, DropBox | Plant> = {}

	protected constructor(props:any) {
	    this.id = props.id;
	    this.$el = props.element;

	    this.creatDropEvent();
	    this.mouseDownReg();
	    this.creatLiveList(props);

	    this.$el.setAttribute('dropBox', this.id);
	    DropBox.dropBoxList[this.id] = this;
	}

	// 生命周期三阶段触发事件
	abstract beforeDropDeal(e: MouseEvent): Promise<void>
	abstract dropMoveDeal(e: MouseEvent): void
	abstract dropOverDeal(e: MouseEvent): Promise<void>

	// 触发开始事件
	abstract mouseDownReg(): void

	/**
	 * 初始化生命周期数组
	 * @param props
	 */
	creatLiveList(props: any): void {
	    const dealFn = (listName: string) => {
	        props[listName] &&
			Object.prototype.toString.call(props[listName]) === '[object Array]' &&
			this.changeDealList(listName, props[listName], DealListDealType.add);
	    };

	    dealFn('beforeDropList');
	    dealFn('dropMoveList');
	    dealFn('dropOverList');
	}

	/**
	 * 初始化拖动event事件流程
	 */
	creatDropEvent(): void {
	    this.beforeDropBind = this.beforeDrop.bind(this);
	    this.dropMoveBind = this.dropMove.bind(this);
	    this.dropOverBind = this.dropOver.bind(this);
	}

	/**
	 * 拖动前
	 */
	async beforeDrop(e: MouseEvent): Promise<void> {
	    // 触发生命周期
	    if (!await this.dealListFn('beforeDropList', e)) {
		    console.log('被生命周期时间阻止');
	        return ;
	    }
	    await this.beforeDropDeal(e);

	    document.body.addEventListener('mousemove', this.dropMoveBind);
	    document.body.addEventListener('mouseup', this.dropOverBind);
	}

	/**
	 * 拖动时
	 */
	dropMove(e: MouseEvent): void {
	    if (!this.dealListFn('dropMoveList', e, true)) {
	        console.log('被生命周期时间阻止');
	        return;
	    }
	    this.dropMoveDeal(e);
	}

	/**
	 * 拖动后
	 */
	async dropOver(e: MouseEvent): Promise<void> {
	    document.body.removeEventListener('mousemove', this.dropMoveBind);
	    document.body.removeEventListener('mouseup', this.dropOverBind);
	    await this.dropOverDeal(e);

	    if (!await this.dealListFn('dropOverList', e)) {
		    console.log('被生命周期时间阻止');
	        return ;
	    }
	}

	/**
	 * 处理生命周期方法函数
	 * @param dealListName
	 * @param e
	 * @param noAsync
	 * @returns {Promise | boolean}
	 */
	dealListFn(dealListName: string, e: MouseEvent, noAsync?: boolean): Promise<boolean> | boolean{
	    if (noAsync) {
	        for (const Fn of this[dealListName]) {
	            const check = (Fn as EventFunction).bind(this)(e);
	            if (typeof check === 'boolean' && !check) {
	                return false;
	            }
	        }
	        return true;
	    } else {
	        return new Promise(async (resolve) => {
	            for (const Fn of this[dealListName]) {
	                const check = await (Fn as EventFunction).bind(this)(e);
	                if (typeof check === 'boolean' && !check) {
	                    resolve(false);
	                    return;
	                }
	            }
	            resolve(true);
	        });
	    }
	}

	/**
	 * 处理改变dealList生命周期方法
	 * @param dealListName
	 * @param dealList
	 * @param dealType
	 */
	changeDealList(dealListName: string, dealList: EventFunction[], dealType: DealListDealType): void {
	    // 过滤不为方法的数组内容
	    dealList = dealList.filter((item) => typeof item === 'function');
	    switch (dealType) {
	        case DealListDealType.add:
	            this[dealListName] = this[dealListName].concat(dealList);
	            break;
	        case DealListDealType.del:
	            this[dealListName] = this[dealListName].filter((item: EventFunction) => !dealList.includes(item));
	    }
	}

	/**
	 * 获取可拖动控件
	 * @param id
	 * @returns {DropBox | Plant}
	 */
	static getDropBox(id: string): DropBox | Plant {
	    return this.dropBoxList[id];
	}

	/**
	 * 将plant注册到dropBoxList
	 * @param plant
	 */
	static setDropBox(plant: Plant): void{
	    this.dropBoxList[plant.id] = plant;
	}
}
